#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""医学图像处理GUI程序

本程序使用PyQt5和SimpleITK实现医学图像的查看和处理功能
"""

import sys
import numpy as np
import SimpleITK as sitk

from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QPushButton, QFileDialog, QComboBox, QLabel, QLineEdit,
    QGridLayout, QMessageBox, QAction, QScrollArea, QSizePolicy, QSlider
)
from PyQt5.QtCore import Qt, QSize, QEvent
from PyQt5.QtGui import QImage, QPixmap, QPainter, QColor


class MedicalImageViewer(QMainWindow):
    """医学图像查看器主窗口类"""
    
    def __init__(self):
        """初始化主窗口"""
        super().__init__()
        
        # 鼠标交互相关
        self.is_right_mouse_down = False
        self.last_mouse_y = 0
        
        self.initUI()
        
    def initUI(self):
        """初始化用户界面"""
        # 设置窗口标题和大小
        self.setWindowTitle('医学图像分割工具')
        self.setGeometry(100, 100, 1400, 800)
        
        # 初始化数据
        self.ct_image = None  # SimpleITK Image对象
        self.mask_image = None  # SimpleITK Image对象
        self.original_ct_image = None  # 保存原始数据用于取消处理
        self.ct_array = None  # numpy数组用于处理
        self.mask_array = None  # numpy数组用于处理
        self.current_slice = 0
        self.current_plane = 0  # 0: axial, 1: coronal, 2: sagittal
        self.window_level = 300  # 窗位
        self.window_width = 1200  # 窗宽
        self.zoom_factor = 1.0  # 缩放因子
        
        # 创建中心部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        # 创建主布局 - 水平布局，左侧图像区域，右侧参数控制区域
        main_layout = QHBoxLayout(central_widget)
        main_layout.setSpacing(10)
        
        # 创建菜单栏
        self.createMenus()
        
        # ===================== 左侧区域（图像显示） =====================
        left_layout = QVBoxLayout()
        
        # 创建顶部按钮区域
        button_layout = QHBoxLayout()
        
        # 导入CT图像按钮
        self.import_ct_btn = QPushButton('导入CT图像')
        self.import_ct_btn.clicked.connect(self.importCTImage)
        button_layout.addWidget(self.import_ct_btn)
        
        # 导入Mask图像按钮
        self.import_mask_btn = QPushButton('导入Mask图像')
        self.import_mask_btn.clicked.connect(self.importMaskImage)
        button_layout.addWidget(self.import_mask_btn)
        
        # 切面选择下拉框
        self.plane_combo = QComboBox()
        self.plane_combo.addItems(['横断面', '冠状面', '矢状面'])
        self.plane_combo.currentIndexChanged.connect(self.onPlaneChanged)
        button_layout.addWidget(QLabel('选择切面:'))
        button_layout.addWidget(self.plane_combo)
        
        # 切片滑动控制
        self.slice_layout = QHBoxLayout()
        self.prev_slice_btn = QPushButton('上一切片')
        self.prev_slice_btn.clicked.connect(self.prevSlice)
        self.slice_label = QLabel('切片: 0')
        self.next_slice_btn = QPushButton('下一切片')
        self.next_slice_btn.clicked.connect(self.nextSlice)
        self.slice_layout.addWidget(self.prev_slice_btn)
        self.slice_layout.addWidget(self.slice_label)
        self.slice_layout.addWidget(self.next_slice_btn)
        
        button_layout.addLayout(self.slice_layout)
        button_layout.addStretch()
        
        # 添加按钮区域到左侧布局
        left_layout.addLayout(button_layout)
        
        # 创建滚动区域用于显示图像
        self.scroll_area = QScrollArea()
        self.scroll_area.setWidgetResizable(True)
        self.scroll_area.setAlignment(Qt.AlignCenter)
        
        # 创建图像显示标签
        self.image_label = QLabel()
        self.image_label.setAlignment(Qt.AlignCenter)
        self.image_label.setMinimumSize(600, 400)
        self.image_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        
        self.scroll_area.setWidget(self.image_label)
        left_layout.addWidget(self.scroll_area, 1)
        
        # 添加重置缩放按钮到左下角
        bottom_layout = QHBoxLayout()
        self.reset_zoom_btn = QPushButton('重置缩放')
        self.reset_zoom_btn.clicked.connect(self.resetZoom)
        bottom_layout.addWidget(self.reset_zoom_btn)
        bottom_layout.addStretch()
        left_layout.addLayout(bottom_layout)
        
        # 添加左侧区域到主布局
        main_layout.addLayout(left_layout, 2)  # 占据2/3的空间
        
        # ===================== 右侧区域（参数控制） =====================
        right_layout = QVBoxLayout()
        right_layout.setSpacing(15)
        
        # 创建右侧控制面板的标题
        right_title = QLabel('参数控制')
        right_title.setStyleSheet('font-weight: bold; font-size: 14px;')
        right_title.setAlignment(Qt.AlignCenter)
        right_layout.addWidget(right_title)
        
        # Cursor Inspector面板
        cursor_inspector_layout = QVBoxLayout()
        cursor_inspector_layout.setContentsMargins(10, 10, 10, 10)
        cursor_inspector_layout.setSpacing(8)
        
        # Inspector标题
        inspector_title = QLabel('Cursor Inspector')
        inspector_title.setStyleSheet('font-weight: bold;')
        inspector_title.setAlignment(Qt.AlignCenter)
        cursor_inspector_layout.addWidget(inspector_title)
        
        # 创建信息显示网格布局
        cursor_info_layout = QGridLayout()
        cursor_info_layout.setSpacing(5)
        
        # 创建垂直BoxLayout用于信息显示
        info_vlayout = QVBoxLayout()
        
        # 创建第一行：XYZ坐标
        coords_row = QHBoxLayout()
        coords_row.setSpacing(5)
        coords_row.addWidget(QLabel('坐标值:'))
        
        # X坐标
        self.cursor_x_label = QLabel('')
        self.cursor_x_label.setFixedWidth(60)
        coords_row.addWidget(self.cursor_x_label)
        
        # Y坐标
        self.cursor_y_label = QLabel('')
        self.cursor_y_label.setFixedWidth(60)
        coords_row.addWidget(self.cursor_y_label)
        
        # Z坐标
        self.cursor_z_label = QLabel('')
        self.cursor_z_label.setFixedWidth(60)
        coords_row.addWidget(self.cursor_z_label)
        
        # 创建第二行：间距显示
        spacing_row = QHBoxLayout()
        spacing_row.setSpacing(5)
        spacing_row.addWidget(QLabel('间距值:'))
        
        # X方向间距
        self.cursor_spacing_x_label = QLabel('')
        self.cursor_spacing_x_label.setFixedWidth(60)
        spacing_row.addWidget(self.cursor_spacing_x_label)
        
        # Y方向间距
        self.cursor_spacing_y_label = QLabel('')
        self.cursor_spacing_y_label.setFixedWidth(60)
        spacing_row.addWidget(self.cursor_spacing_y_label)
        
        # Z方向间距
        self.cursor_spacing_z_label = QLabel('')
        self.cursor_spacing_z_label.setFixedWidth(60)
        spacing_row.addWidget(self.cursor_spacing_z_label)
        
        # 创建第三行：像素值和标签值显示
        values_row = QHBoxLayout()
        values_row.setSpacing(5)
        
        # 像素值
        values_row.addWidget(QLabel('像素值:'))
        self.cursor_value_label = QLabel('')
        self.cursor_value_label.setFixedWidth(80)
        values_row.addWidget(self.cursor_value_label)
        
        # 标签值（mask值）
        values_row.addWidget(QLabel('标签值:'))
        self.cursor_mask_label = QLabel('')
        self.cursor_mask_label.setFixedWidth(80)
        values_row.addWidget(self.cursor_mask_label)
        
        # 添加三行到垂直布局
        info_vlayout.addLayout(coords_row)
        info_vlayout.addLayout(spacing_row)
        info_vlayout.addLayout(values_row)
        
        # 创建容器widget来容纳垂直布局
        info_widget = QWidget()
        info_widget.setLayout(info_vlayout)
        cursor_info_layout.addWidget(info_widget, 0, 1)
        
        # 添加网格布局到Inspector面板
        cursor_inspector_layout.addLayout(cursor_info_layout)
        
        # 美化Inspector面板 - 添加边框和背景色
        inspector_widget = QWidget()
        inspector_widget.setLayout(cursor_inspector_layout)
        inspector_widget.setStyleSheet('background-color: #f5f5f5; border: 1px solid #ccc; border-radius: 4px;')
        
        right_layout.addWidget(inspector_widget)
        
        # 窗宽窗位控制
        window_group_layout = QVBoxLayout()
        window_group_layout.setContentsMargins(10, 10, 10, 10)
        window_group_layout.setSpacing(8)
        window_group_layout.addWidget(QLabel('窗宽窗位调整:'))
        
        window_params_layout = QGridLayout()
        
        # 窗宽设置
        window_params_layout.addWidget(QLabel('窗宽:'), 0, 0)
        self.window_width_edit = QLineEdit(str(self.window_width))
        self.window_width_edit.editingFinished.connect(self.onWindowWidthChanged)
        self.window_width_edit.setFixedWidth(100)
        window_params_layout.addWidget(self.window_width_edit, 0, 1)
        
        # 窗宽滑块
        self.window_width_slider = QSlider(Qt.Horizontal)
        self.window_width_slider.setMinimum(1)
        self.window_width_slider.setMaximum(4000)
        self.window_width_slider.setValue(int(self.window_width))
        self.window_width_slider.setSingleStep(1)
        self.window_width_slider.setPageStep(50)
        self.window_width_slider.setFixedWidth(200)
        self.window_width_slider.valueChanged.connect(self.onWindowWidthSliderChanged)
        window_params_layout.addWidget(self.window_width_slider, 0, 2)
        
        # 窗位设置
        window_params_layout.addWidget(QLabel('窗位:'), 1, 0)
        self.window_level_edit = QLineEdit(str(self.window_level))
        self.window_level_edit.editingFinished.connect(self.onWindowLevelChanged)
        self.window_level_edit.setFixedWidth(100)
        window_params_layout.addWidget(self.window_level_edit, 1, 1)
        
        # 窗位滑块
        self.window_level_slider = QSlider(Qt.Horizontal)
        self.window_level_slider.setMinimum(-1000)
        self.window_level_slider.setMaximum(1000)
        self.window_level_slider.setValue(int(self.window_level))
        self.window_level_slider.setSingleStep(1)
        self.window_level_slider.setPageStep(50)
        self.window_level_slider.setFixedWidth(200)
        self.window_level_slider.valueChanged.connect(self.onWindowLevelSliderChanged)
        window_params_layout.addWidget(self.window_level_slider, 1, 2)
        
        window_group_layout.addLayout(window_params_layout)
        right_layout.addLayout(window_group_layout)
        
        # 参数输入区域
        params_group_layout = QVBoxLayout()
        params_group_layout.setContentsMargins(10, 10, 10, 10)
        params_group_layout.setSpacing(8)
        params_group_layout.addWidget(QLabel('处理参数设置:'))
        
        params_grid_layout = QGridLayout()
        self.param_inputs = []
        self.param_sliders = []
        for i in range(5):
            params_grid_layout.addWidget(QLabel(f'参数{i+1}:'), i, 0)
            param_input = QLineEdit('0.5')
            param_input.setFixedWidth(80)
            self.param_inputs.append(param_input)
            params_grid_layout.addWidget(param_input, i, 1)
            
            # 参数滑块
            param_slider = QSlider(Qt.Horizontal)
            param_slider.setMinimum(0)
            param_slider.setMaximum(100)
            param_slider.setValue(50)  # 默认0.5对应50
            param_slider.setSingleStep(1)
            param_slider.setFixedWidth(200)
            param_slider.valueChanged.connect(lambda value, idx=i: self.onParamSliderChanged(idx, value))
            self.param_sliders.append(param_slider)
            params_grid_layout.addWidget(param_slider, i, 2)
        
        params_group_layout.addLayout(params_grid_layout)
        right_layout.addLayout(params_group_layout)
        
        # 处理按钮
        process_group_layout = QVBoxLayout()
        process_group_layout.setContentsMargins(10, 10, 10, 10)
        process_group_layout.setSpacing(10)
        
        self.process_btn = QPushButton('执行处理')
        self.process_btn.clicked.connect(self.processImage)
        self.process_btn.setEnabled(False)
        self.process_btn.setMinimumHeight(40)
        
        self.undo_btn = QPushButton('取消处理')
        self.undo_btn.clicked.connect(self.undoProcess)
        self.undo_btn.setEnabled(False)
        self.undo_btn.setMinimumHeight(40)
        
        process_group_layout.addWidget(self.process_btn)
        process_group_layout.addWidget(self.undo_btn)
        
        right_layout.addLayout(process_group_layout)
        
        # 添加伸展项，使右侧面板内容保持在上部
        right_layout.addStretch()
        
        # 添加右侧区域到主布局
        main_layout.addLayout(right_layout, 1)  # 占据1/3的空间
    
    def createMenus(self):
        """创建菜单栏"""
        # 创建菜单栏
        menubar = self.menuBar()
        
        # 文件菜单
        file_menu = menubar.addMenu('文件')
        
        # 导入CT图像动作
        import_ct_action = QAction('导入CT图像', self)
        import_ct_action.setShortcut('Ctrl+O')
        import_ct_action.triggered.connect(self.importCTImage)
        file_menu.addAction(import_ct_action)
        
        # 导入Mask图像动作
        import_mask_action = QAction('导入Mask图像', self)
        import_mask_action.setShortcut('Ctrl+Shift+O')
        import_mask_action.triggered.connect(self.importMaskImage)
        file_menu.addAction(import_mask_action)
        
        # 退出动作
        exit_action = QAction('退出', self)
        exit_action.setShortcut('Ctrl+Q')
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)
        
        # 编辑菜单
        edit_menu = menubar.addMenu('编辑')
        
        # 撤销处理动作
        undo_action = QAction('撤销处理', self)
        undo_action.setShortcut('Ctrl+Z')
        undo_action.triggered.connect(self.undoProcess)
        edit_menu.addAction(undo_action)
        
        # 查看菜单
        view_menu = menubar.addMenu('查看')
        
        # 放大动作
        zoom_in_action = QAction('放大', self)
        zoom_in_action.setShortcut('Ctrl++')
        zoom_in_action.triggered.connect(self.zoomIn)
        view_menu.addAction(zoom_in_action)
        
        # 缩小动作
        zoom_out_action = QAction('缩小', self)
        zoom_out_action.setShortcut('Ctrl+-')
        zoom_out_action.triggered.connect(self.zoomOut)
        view_menu.addAction(zoom_out_action)
        
        # 重置缩放动作
        reset_zoom_action = QAction('重置缩放', self)
        reset_zoom_action.setShortcut('Ctrl+0')
        reset_zoom_action.triggered.connect(self.resetZoom)
        view_menu.addAction(reset_zoom_action)
        
        # 帮助菜单
        help_menu = menubar.addMenu('帮助')
        
        # 关于动作
        about_action = QAction('关于', self)
        about_action.triggered.connect(self.showAbout)
        help_menu.addAction(about_action)
    
    def importCTImage(self):
        """导入CT图像"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择CT图像", "", "Nifti文件 (*.nii *.nii.gz)"
        )
        
        if file_path:
            try:
                # 使用SimpleITK加载图像
                self.ct_image = sitk.ReadImage(file_path)
                self.original_ct_image = sitk.Image(self.ct_image)
                
                # 转换为numpy数组用于处理
                self.ct_array = sitk.GetArrayFromImage(self.ct_image)
                
                # 重置当前切片和缩放
                self.current_slice = 0
                self.zoom_factor = 1.0
                self.updateSliceLabel()
                
                # 更新图像显示
                self.updateImageDisplay()
                
                # 启用处理按钮
                self.process_btn.setEnabled(True)
                
                # 获取并显示像素间距
                spacing = self.ct_image.GetSpacing()
                self.cursor_spacing_x_label.setText(f"{spacing[0]:.3f}")
                self.cursor_spacing_y_label.setText(f"{spacing[1]:.3f}")
                self.cursor_spacing_z_label.setText(f"{spacing[2]:.3f}")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"导入CT图像失败: {str(e)}")
    
    def importMaskImage(self):
        """导入Mask图像"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择Mask图像", "", "Nifti文件 (*.nii *.nii.gz)"
        )
        
        if file_path:
            try:
                # 使用SimpleITK加载图像
                self.mask_image = sitk.ReadImage(file_path)
                
                # 转换为numpy数组用于处理
                self.mask_array = sitk.GetArrayFromImage(self.mask_image)
                
                # 检查是否与CT图像尺寸匹配
                if self.ct_array is not None:
                    if self.mask_array.shape != self.ct_array.shape:
                        QMessageBox.warning(self, "警告", "Mask图像尺寸与CT图像不匹配")
                
                # 更新图像显示
                self.updateImageDisplay()
            
            except Exception as e:
                QMessageBox.critical(self, "错误", f"导入Mask图像失败: {str(e)}")
    
    def onPlaneChanged(self, index):
        """切换显示平面（轴位面、冠状面、矢状面）"""
        try:
            self.current_plane = index
            # 确保切片索引在有效范围内
            if self.ct_array is not None:
                if self.current_plane == 0:  # 轴位面
                    self.current_slice = min(self.current_slice, self.ct_array.shape[0] - 1)
                elif self.current_plane == 1:  # 冠状面
                    self.current_slice = min(self.current_slice, self.ct_array.shape[1] - 1)
                elif self.current_plane == 2:  # 矢状面
                    self.current_slice = min(self.current_slice, self.ct_array.shape[2] - 1)
                
                self.current_slice = max(0, self.current_slice)
            else:
                self.current_slice = 0  # 重置到第一个切片
            
            self.zoom_factor = 1.0  # 重置缩放
            self.updateSliceLabel()
            self.updateImageDisplay()
        except Exception as e:
            QMessageBox.critical(self, "错误", f"切换切面时出错: {str(e)}")
            # 重置到默认轴位面
            self.current_plane = 0
            self.current_slice = 0
            self.plane_combo.setCurrentIndex(0)
    
    def prevSlice(self):
        """显示上一切片"""
        if self.ct_array is not None:
            if self.current_slice > 0:
                self.current_slice -= 1
                self.updateSliceLabel()
                self.updateImageDisplay()
    
    def nextSlice(self):
        """显示下一切片"""
        if self.ct_array is not None:
            max_slice = self.getMaxSlice()
            if self.current_slice < max_slice - 1:
                self.current_slice += 1
                self.updateSliceLabel()
                self.updateImageDisplay()
    
    def getMaxSlice(self):
        """获取当前切面的最大切片数"""
        if self.ct_array is None:
            return 0
        
        if self.current_plane == 0:  # axial
            return self.ct_array.shape[0]  # SimpleITK数组顺序是 (z, y, x)
        elif self.current_plane == 1:  # coronal
            return self.ct_array.shape[1]
        else:  # sagittal
            return self.ct_array.shape[2]
    
    def updateSliceLabel(self):
        """更新切片标签显示"""
        max_slice = self.getMaxSlice()
        self.slice_label.setText(f'切片: {self.current_slice + 1}/{max_slice}')
    
    def onWindowWidthChanged(self):
        """窗宽改变时更新显示"""
        try:
            self.window_width = float(self.window_width_edit.text())
            # 同步滑块值
            self.window_width_slider.setValue(int(self.window_width))
            self.updateImageDisplay()
        except ValueError:
            QMessageBox.warning(self, "警告", "窗宽必须是有效的数字")
            self.window_width_edit.setText(str(self.window_width))
    
    def onWindowLevelChanged(self):
        """窗位改变时更新显示"""
        try:
            self.window_level = float(self.window_level_edit.text())
            # 同步滑块值
            self.window_level_slider.setValue(int(self.window_level))
            self.updateImageDisplay()
        except ValueError:
            QMessageBox.warning(self, "警告", "窗位必须是有效的数字")
            self.window_level_edit.setText(str(self.window_level))
    
    def onWindowWidthSliderChanged(self, value):
        """窗宽滑块改变时实时更新显示"""
        self.window_width = value
        self.window_width_edit.setText(str(value))
        self.updateImageDisplay()
    
    def onWindowLevelSliderChanged(self, value):
        """窗位滑块改变时实时更新显示"""
        self.window_level = value
        self.window_level_edit.setText(str(value))
        self.updateImageDisplay()
    
    def onParamSliderChanged(self, param_idx, value):
        """参数滑块改变时更新参数值并实时处理图像"""
        # 将0-100的滑块值映射到0-1的参数值
        param_value = value / 100.0
        self.param_inputs[param_idx].setText(f"{param_value:.2f}")
        
        # 实时处理图像（仅当第一个参数变化时，因为当前处理逻辑只使用第一个参数）
        if param_idx == 0 and self.ct_array is not None:
            self.realtimeProcessImage()
    
    def realtimeProcessImage(self):
        """实时处理图像，用于滑块调整时"""
        try:
            # 获取参数值
            params = []
            for i, input_box in enumerate(self.param_inputs):
                try:
                    params.append(float(input_box.text()))
                except ValueError:
                    return  # 如果参数无效，直接返回
            
            # 使用第一个参数作为阈值进行二值化处理
            threshold = params[0]
            
            # 保存当前数据状态用于撤销
            if self.original_ct_image is not None:
                # 创建一个临时副本进行处理，不直接修改original_ct_image
                current_image = sitk.Image(self.original_ct_image)
                current_array = sitk.GetArrayFromImage(current_image)
                
                # 对CT数据进行二值化处理
                # 首先归一化CT值到0-1范围
                min_val = np.min(current_array)
                max_val = np.max(current_array)
                normalized_data = (current_array - min_val) / (max_val - min_val)
                
                # 应用二值化
                binary_data = normalized_data > threshold
                
                # 转换回原始范围
                processed_array = binary_data * (max_val - min_val) + min_val
                
                # 更新数据
                self.ct_array = processed_array
                self.ct_image = sitk.GetImageFromArray(self.ct_array)
                self.ct_image.CopyInformation(self.original_ct_image)
                
                # 更新图像显示
                self.updateImageDisplay()
                
                # 启用撤销按钮
                self.undo_btn.setEnabled(True)
        
        except Exception as e:
            # 实时处理出错时静默失败，不显示错误消息
            pass
    
    def zoomIn(self, custom_step=None):
        """缩小视角（放大图像内容），保持显示尺寸不变"""
        if custom_step is not None:
            # 自定义缩放步长（用于鼠标右键拖动）
            self.zoom_factor = min(10.0, self.zoom_factor * (1 + custom_step))
        else:
            # 默认固定步长
            if self.zoom_factor < 10.0:
                self.zoom_factor *= 1.25
        self.updateImageDisplay()
    
    def zoomOut(self, custom_step=None):
        """扩大视角（缩小图像内容），保持显示尺寸不变"""
        if custom_step is not None:
            # 自定义缩放步长（用于鼠标右键拖动）
            self.zoom_factor = max(0.1, self.zoom_factor / (1 + custom_step))
        else:
            # 默认固定步长
            if self.zoom_factor > 0.1:
                self.zoom_factor /= 1.25
        self.updateImageDisplay()
    
    def resetZoom(self):
        """重置缩放比例为100%"""
        self.zoom_factor = 1.0
        if self.ct_array is not None:
            self.updateImageDisplay()
    
    def updateImageDisplay(self):
        """更新图像显示，直接使用PyQt5进行图像处理和显示，保持图像长宽比与原图一致，缩放时保持显示尺寸"""
        if self.ct_array is None or len(self.ct_array.shape) < 3:
            return
        
        # 获取当前切片，确保保持int16类型
        if self.current_plane == 0:  # axial
            if self.current_slice < 0 or self.current_slice >= self.ct_array.shape[0]:
                return
            ct_slice = self.ct_array[self.current_slice, :, :].astype(np.int16)
            mask_slice = self.mask_array[self.current_slice, :, :] if self.mask_array is not None else None
        elif self.current_plane == 1:  # coronal
            if self.current_slice < 0 or self.current_slice >= self.ct_array.shape[1]:
                return
            ct_slice = self.ct_array[:, self.current_slice, :].astype(np.int16)
            mask_slice = self.mask_array[:, self.current_slice, :] if self.mask_array is not None else None
        else:  # sagittal
            if self.current_slice < 0 or self.current_slice >= self.ct_array.shape[2]:
                return
            ct_slice = self.ct_array[:, :, self.current_slice].astype(np.int16)
            mask_slice = self.mask_array[:, :, self.current_slice] if self.mask_array is not None else None
        
        # 保存原始CT切片值用于显示像素值
        self.current_ct_slice = ct_slice
        
        # 应用窗宽窗位调整
        min_val = self.window_level - self.window_width / 2
        max_val = self.window_level + self.window_width / 2
        
        # 将CT值映射到0-255范围
        windowed = np.clip(ct_slice, min_val, max_val)
        windowed = 255 * (windowed - min_val) / (max_val - min_val)
        windowed = windowed.astype(np.uint8)
        
        # 根据切面方向进行适当的旋转和转置
        if self.current_plane == 1:  # 冠状面需要旋转
            windowed = np.rot90(windowed, 1)
            if mask_slice is not None:
                mask_slice = np.rot90(mask_slice, 1)
        elif self.current_plane == 2:  # 矢状面需要旋转和转置
            windowed = np.rot90(windowed, 2)
            if mask_slice is not None:
                mask_slice = np.rot90(mask_slice, 2)
        
        # 创建QImage
        height, width = windowed.shape
        bytes_per_line = width
        q_image = QImage(windowed.data, width, height, bytes_per_line, QImage.Format_Grayscale8)
        
        # 创建QPixmap用于显示
        pixmap = QPixmap.fromImage(q_image)
        
        # 如果有mask，将其叠加到图像上
        if mask_slice is not None:
            # 创建mask的QPixmap
            mask_qimage = QImage(mask_slice.astype(np.uint8).data, 
                               mask_slice.shape[1], mask_slice.shape[0], 
                               mask_slice.shape[1], QImage.Format_Grayscale8)
            mask_pixmap = QPixmap.fromImage(mask_qimage)
            
            # 创建结果pixmap并使用QPainter叠加mask
            result_pixmap = pixmap.copy()
            painter = QPainter(result_pixmap)
            
            # 设置红色为mask颜色，半透明
            painter.setCompositionMode(QPainter.CompositionMode_SourceOver)
            painter.fillRect(result_pixmap.rect(), QColor(255, 0, 0, 0))  # 透明背景
            
            # 绘制mask（白色区域）
            painter.drawPixmap(0, 0, mask_pixmap.scaled(result_pixmap.size(), Qt.KeepAspectRatio))
            
            # 设置半透明效果
            painter.setOpacity(0.5)
            painter.fillRect(result_pixmap.rect(), QColor(255, 0, 0, 128))
            
            painter.end()
            pixmap = result_pixmap
        
        # 获取图像显示区域的尺寸（保持固定）
        display_size = self.image_label.size()
        if display_size.width() == 0 or display_size.height() == 0:
            # 如果标签还没初始化大小，使用默认大小
            display_size = QSize(600, 600)
        
        # 根据缩放因子计算要显示的图像区域（缩小视角）
        # zoom_factor = 1.0 表示显示完整图像
        # zoom_factor > 1.0 表示放大（显示图像的一部分）
        # zoom_factor < 1.0 表示缩小（显示更大范围）
        
        # 计算裁剪区域
        if self.zoom_factor >= 1.0:
            # 放大时，计算要裁剪的区域
            scale = 1.0 / self.zoom_factor
            crop_width = int(width * scale)
            crop_height = int(height * scale)
            
            # 确保裁剪区域不小于1x1像素
            crop_width = max(1, crop_width)
            crop_height = max(1, crop_height)
            
            # 计算裁剪区域的左上角坐标（居中裁剪）
            x = (width - crop_width) // 2
            y = (height - crop_height) // 2
            
            # 裁剪图像
            if crop_width < width or crop_height < height:
                cropped_pixmap = pixmap.copy(x, y, crop_width, crop_height)
            else:
                cropped_pixmap = pixmap
            
            # 缩放到显示尺寸，保持长宽比
            # 使用FastTransformation实现像素化效果（每个像素显示为小方块）
            scaled_pixmap = cropped_pixmap.scaled(display_size, Qt.KeepAspectRatio, Qt.FastTransformation)
        else:
            # 缩小时，直接缩放整个图像到显示尺寸，保持长宽比
            # 使用FastTransformation实现像素化效果（每个像素显示为小方块）
            scaled_pixmap = pixmap.scaled(display_size, Qt.KeepAspectRatio, Qt.FastTransformation)
        
        # 设置到标签显示，保持标签大小固定
        self.image_label.setPixmap(scaled_pixmap)
        self.image_label.setFixedSize(display_size)
    
    def processImage(self):
        """处理图像"""
        if self.ct_array is None:
            return
        
        try:
            # 获取参数值
            params = []
            for i, input_box in enumerate(self.param_inputs):
                try:
                    params.append(float(input_box.text()))
                except ValueError:
                    QMessageBox.warning(self, "警告", f"参数{i+1}不是有效的数字")
                    return
            
            # 使用第一个参数作为阈值进行二值化处理
            threshold = params[0]
            
            # 保存当前数据状态用于撤销
            if self.original_ct_image is not None:
                self.ct_image = sitk.Image(self.original_ct_image)
                self.ct_array = sitk.GetArrayFromImage(self.ct_image)
            
            # 对CT数据进行二值化处理
            # 首先归一化CT值到0-1范围
            min_val = np.min(self.ct_array)
            max_val = np.max(self.ct_array)
            normalized_data = (self.ct_array - min_val) / (max_val - min_val)
            
            # 应用二值化
            binary_data = normalized_data > threshold
            
            # 转换回原始范围
            processed_array = binary_data * (max_val - min_val) + min_val
            
            # 更新数据
            self.ct_array = processed_array
            self.ct_image = sitk.GetImageFromArray(self.ct_array)
            self.ct_image.CopyInformation(self.original_ct_image)
            
            # 更新图像显示
            self.updateImageDisplay()
            
            # 启用撤销按钮
            self.undo_btn.setEnabled(True)
                        
        except Exception as e:
            QMessageBox.critical(self, "错误", f"处理图像失败: {str(e)}")
    
    def undoProcess(self):
        """撤销处理，恢复原始图像"""
        if self.original_ct_image is not None:
            self.ct_image = sitk.Image(self.original_ct_image)
            self.ct_array = sitk.GetArrayFromImage(self.ct_image)
            self.updateImageDisplay()
            self.undo_btn.setEnabled(False)
    
    def showAbout(self):
        """显示关于对话框"""
        QMessageBox.about(
            self, 
            "关于", 
            "医学图像分割工具 v3.0\n\n"  
            "功能：\n"  
            "- 使用SimpleITK和PyQt5直接处理和显示图像\n"  
            "- 支持窗宽窗位调整\n"  
            "- 三种切面（横冠矢）显示\n"  
            "- 图像缩放功能\n"  
            "- 参数调整和图像二值化处理\n"  
            "- 支持撤销操作\n\n"  
            "© 2024 医学图像处理工具"
        )
    
    def mousePressEvent(self, event):
        """鼠标按下事件处理 - 左键点击显示像素值，右键拖动缩放"""
        if event.button() == Qt.RightButton:
            self.is_right_mouse_down = True
            self.last_mouse_y = event.y()
        elif event.button() == Qt.LeftButton and self.ct_array is not None:
            # 左键点击时显示像素值
            self.updatePixelValueDisplay(event)
        super().mousePressEvent(event)
    
    def mouseReleaseEvent(self, event):
        """鼠标释放事件处理"""
        if event.button() == Qt.RightButton:
            self.is_right_mouse_down = False
        super().mouseReleaseEvent(event)
    
    def mouseMoveEvent(self, event):
        """鼠标移动事件处理 - 右键上下滑动缩放，实时显示鼠标位置像素值"""
        # 处理右键拖动缩放
        if self.is_right_mouse_down and self.ct_array is not None:
            current_y = event.y()
            # 计算鼠标移动距离
            delta_y = self.last_mouse_y - current_y
            
            # 根据移动距离调整缩放
            zoom_step = 0.01  # 缩放步长
            if delta_y > 0:  # 向上移动，放大
                self.zoomIn(zoom_step * abs(delta_y))
            elif delta_y < 0:  # 向下移动，缩小
                self.zoomOut(zoom_step * abs(delta_y))
            
            self.last_mouse_y = current_y
        
        # 实时显示鼠标位置的像素值
        self.updatePixelValueDisplay(event)
        
        super().mouseMoveEvent(event)
    
    def updatePixelValueDisplay(self, event):
        """更新鼠标位置的像素值显示到Cursor Inspector面板"""
        if hasattr(self, 'current_ct_slice') and self.current_ct_slice is not None:
            # 转换全局坐标到图像标签局部坐标
            label_pos = self.image_label.mapFromGlobal(self.mapToGlobal(event.pos()))
            
            # 检查鼠标是否在图像标签内
            if self.image_label.rect().contains(label_pos):
                # 获取图像尺寸和显示尺寸
                img_height, img_width = self.current_ct_slice.shape
                
                # 获取显示区域的pixmap
                if self.image_label.pixmap() is not None:
                    pixmap_size = self.image_label.pixmap().size()
                    
                    # 计算图像在标签中的实际位置和大小（考虑居中对齐）
                    label_rect = self.image_label.rect()
                    x_offset = (label_rect.width() - pixmap_size.width()) // 2
                    y_offset = (label_rect.height() - pixmap_size.height()) // 2
                    
                    # 调整鼠标位置到pixmap坐标系
                    px = label_pos.x() - x_offset
                    py = label_pos.y() - y_offset
                    
                    # 检查调整后的坐标是否在pixmap范围内
                    if 0 <= px < pixmap_size.width() and 0 <= py < pixmap_size.height():
                        # 处理缩放情况，计算原始图像坐标
                        if self.zoom_factor >= 1.0:
                            # 放大状态，需要计算裁剪区域和缩放比例
                            scale = 1.0 / self.zoom_factor
                            crop_width = int(img_width * scale)
                            crop_height = int(img_height * scale)
                            crop_x = (img_width - crop_width) // 2
                            crop_y = (img_height - crop_height) // 2
                            
                            # 映射显示坐标到裁剪区域
                            img_x = int(px * crop_width / pixmap_size.width()) + crop_x
                            img_y = int(py * crop_height / pixmap_size.height()) + crop_y
                        else:
                            # 缩小状态，直接映射
                            img_x = int(px * img_width / pixmap_size.width())
                            img_y = int(py * img_height / pixmap_size.height())
                        
                        # 检查计算出的坐标是否在有效范围内
                        if 0 <= img_x < img_width and 0 <= img_y < img_height:
                            # 获取像素值
                            pixel_value = self.current_ct_slice[img_y, img_x]
                            
                            # 根据切面方向调整显示信息
                            if self.current_plane == 0:  # axial
                                plane_name = "轴位面"
                                x_coord = img_x
                                y_coord = img_y
                                z_coord = self.current_slice
                            elif self.current_plane == 1:  # coronal
                                plane_name = "冠状面"
                                # 对于冠状面，需要考虑旋转带来的坐标变换
                                x_coord = img_height - 1 - img_y
                                y_coord = self.current_slice
                                z_coord = img_x
                            else:  # sagittal
                                plane_name = "矢状面"
                                # 对于矢状面，需要考虑旋转带来的坐标变换
                                x_coord = self.current_slice
                                y_coord = img_height - 1 - img_y
                                z_coord = img_width - 1 - img_x
                            
                            # 更新Cursor Inspector面板的信息
                            self.cursor_x_label.setText(f"{x_coord}")
                            self.cursor_y_label.setText(f"{y_coord}")
                            self.cursor_z_label.setText(f"{z_coord}")
                            self.cursor_value_label.setText(f"{pixel_value}")
                            
                            # 获取mask值
                            mask_value = ""
                            if hasattr(self, 'mask_array') and self.mask_array is not None:
                                # 根据不同切面获取对应的mask值
                                if self.current_plane == 0:  # axial
                                    mask_value = self.mask_array[self.current_slice, img_y, img_x]
                                elif self.current_plane == 1:  # coronal
                                    mask_value = self.mask_array[img_height - 1 - img_y, self.current_slice, img_x]
                                else:  # sagittal
                                    mask_value = self.mask_array[self.current_slice, img_height - 1 - img_y, img_width - 1 - img_x]
                            
                            self.cursor_mask_label.setText(str(mask_value) if mask_value != "" else "")
                            return
        
        # 如果鼠标不在图像上，显示默认信息
        self.cursor_x_label.setText("")
        self.cursor_y_label.setText("")
        self.cursor_z_label.setText("")
        self.cursor_value_label.setText("")
        if hasattr(self, 'cursor_mask_label'):
            self.cursor_mask_label.setText("")
    
    def wheelEvent(self, event):
        """鼠标滚轮事件处理 - 切换上下帧"""
        if self.ct_array is not None:
            # 获取滚轮方向
            delta = event.angleDelta().y()
            if delta < 0:  # 滚轮向下，下一帧
                self.nextSlice()
            else:  # 滚轮向上，上一帧
                self.prevSlice()
    
    def keyPressEvent(self, event):
        """键盘事件处理"""
        if event.key() == Qt.Key_Left:
            self.prevSlice()
        elif event.key() == Qt.Key_Right:
            self.nextSlice()
        elif event.key() == Qt.Key_Z and (event.modifiers() & Qt.ControlModifier):
            self.undoProcess()
        elif event.key() == Qt.Key_Plus and (event.modifiers() & Qt.ControlModifier):
            # Ctrl++ 增加窗宽
            self.window_width = min(2000, self.window_width + 50)
            self.window_width_edit.setText(str(self.window_width))
            self.updateImageDisplay()
        elif event.key() == Qt.Key_Minus and (event.modifiers() & Qt.ControlModifier):
            # Ctrl+- 减少窗宽
            self.window_width = max(50, self.window_width - 50)
            self.window_width_edit.setText(str(self.window_width))
            self.updateImageDisplay()
        elif event.key() == Qt.Key_Equal and (event.modifiers() & Qt.ControlModifier):
            # Ctrl+= 放大
            self.zoomIn()
        elif event.key() == Qt.Key_0 and (event.modifiers() & Qt.ControlModifier):
            # Ctrl+0 重置缩放
            self.resetZoom()
        else:
            super().keyPressEvent(event)


def main():
    """主函数"""
    app = QApplication(sys.argv)
    viewer = MedicalImageViewer()
    viewer.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()